/* 
 * File:   ipd_median_filter.cpp
 * Author: dima
 * 
 * Created on 24 января 2015 г., 17:01
 */
#include "ipd_median_filter.h"
#include "ipd_img.h"
#include "ipd_exception.h"
namespace ipd
{

ipd_median_filter::ipd_median_filter(ipd_img &img, int w_mask, int h_mask) : image(img), width_mask(w_mask), height_mask(h_mask)
{
    shift_width = (width_mask - 1) / 2;
    shift_height = (height_mask - 1) / 2;
    mask = NULL;
}

ipd_median_filter::~ipd_median_filter()
{
    delete_mask();
}

void ipd_median_filter::convert_image(ipd_img &img)//convert Image
{
    width_image = image.get_width(); //width image
    height_image = image.get_height(); //height image
    color_type = image.get_color_type();
    img.init(width_image, height_image, color_type);
    create_mask();
    for (int i = 0; i < height_image; i++)
    {
        for (int j = 0; j < width_image; j++)
        {
            fill_mask(j, i);
            impose_mask(j, i, img);
        }
    }
}

ipd_median_filter::ipd_median_filter(const ipd_median_filter& orig) : image(orig.image)
{
}

void ipd_median_filter::create_mask()//create mask
{
    delete_mask();
    switch (color_type)
    {
    case tp_RGB:
    {
        mask = new unsigned char [width_mask * height_mask * sz_RGB];
        break;
    }
    case tp_mono:
    {
        mask = new unsigned char [width_mask * height_mask];
        break;
    }
    default:
    {
        throw ipd_exception(std::string("Image model is not supported (Supports RGB and monochrome image)"), std::string("ipd_img"));
    }
    }
}

void ipd_median_filter::delete_mask()//delete mask
{
    if (mask != NULL)
        delete mask;
    mask = NULL;
}

void ipd_median_filter::fill_mask(int index_w, int index_h)//fill the mask
{
    int index_x, index_y, index;
    unsigned char *ch;
    for (int i = 0; i < height_mask; i++)
    {
        for (int j = 0; j < width_mask; j++)
        {
            index_y = index_h + i - shift_height;
            index_x = index_w + j - shift_width;
            if (index_x < 0 || index_x >= width_image || index_y < 0 || index_y >= height_image)
                continue;
            ch = image.get_color(index_x, index_y);
            index = i * width_mask + j;
            switch (color_type)
            {
            case tp_RGB:
            {
                mask[index] = ch[0];
                mask[height_mask * width_mask + index] = ch[1];
                mask[2 * height_mask * width_mask + index] = ch[2];
                break;
            }
            case tp_mono:
            {
                mask[index] = *ch;
                break;
            }
            }
        }
    }
    switch (color_type)
    {
    case tp_RGB:
    {
        int step = height_mask * width_mask;
        for (int i = 0; i < sz_RGB; i++)
            sort_mask(i * step, (i + 1) * step - 1);
        break;
    }
    case tp_mono:
    {
        sort_mask(0, height_mask * width_mask - 1);
        break;
    }
    }
}

void ipd_median_filter::impose_mask(int index_w, int index_h, ipd_img &img)//impose a mask
{
    int step = height_mask * width_mask;
    int step_h = (height_mask * width_mask) / (double) 2 + 0.5;
    unsigned char ch[sz_RGB];
    switch (color_type)
    {
    case tp_RGB:
    {
        for (int i = 0; i < sz_RGB; i++)
            ch[i] = mask[i * step + step_h];
        break;
    }
    case tp_mono:
    {
        ch[0] = mask[step_h];
        break;
    }
    }
    img.set_color(index_w, index_h, ch);     
}

void ipd_median_filter::sort_mask(int left, int right)
{
    int i, j;
    bool f;
    unsigned char t;
    if (left >= right)
        return;
    for (i = left, j = right, f = true; i < j; f ? j-- : i++)
        if (mask[i] > mask[j])
        {
            t = mask[i];
            mask[i] = mask[j];
            mask[j] = t;
            f = !f;
        }
    sort_mask(left, i - 1);
    sort_mask(i + 1, right);
}

}